package com.sky.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.CommonConstant;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.*;
import com.sky.entity.*;
import com.sky.exception.BaseException;
import com.sky.exception.OrderBusinessException;
import com.sky.mapper.*;
import com.sky.properties.BaiduMapsProperties;
import com.sky.properties.WeChatProperties;
import com.sky.result.PageResult;
import com.sky.service.OrderService;
import com.sky.utils.BaiduMapsUtil;
import com.sky.utils.WeChatPayUtil;
import com.sky.vo.OrderPaymentVO;
import com.sky.vo.OrderStatisticsVO;
import com.sky.vo.OrderSubmitVO;
import com.sky.vo.OrderVO;
import com.sky.websocket.WebSocketService;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @Description OrderServiceImpl
 * @Author zhuzi
 * @Date 2023/10/05
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    private final OrderMapper orderMapper;

    private final OrderDetailMapper orderDetailMapper;

    private final AddressBookMapper addressBookMapper;

    private final UserMapper userMapper;

    private final ShoppingCartMapper shoppingCartMapper;

    private final WeChatPayUtil weChatPayUtil;

    private final WeChatProperties weChatProperties;

    private final BaiduMapsUtil baiduMapsUtil;

    private final WebSocketService webSocketService;

    private static String MERCHANT_GEOCODING;

    private final BaiduMapsProperties baiduMapsProperties;

    /**
     * 提交订单
     *
     * @param ordersSubmitDTO OrdersSubmitDTO
     * @return OrderSubmitVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
        // 获取商家地理编码
        if (MERCHANT_GEOCODING == null) {
            MERCHANT_GEOCODING = baiduMapsUtil.geocoding(baiduMapsProperties.getAddress());
            if (MERCHANT_GEOCODING == null) {
                throw new OrderBusinessException(MessageConstant.MERCHANT_ADDRESS_RESOLUTION_FAILED);
            }
            log.info("Get merchantGeocoding: {}", MERCHANT_GEOCODING);
        }

        // 获取地址信息
        AddressBook addressBook = addressBookMapper.getAddressById(ordersSubmitDTO.getAddressBookId());
        if (addressBook == null || addressBook.getId() == null) {
            throw new OrderBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }

        // 获取用户地理编码
        String address = addressBook.getProvinceName() + addressBook.getCityName() + addressBook.getDistrictName() + addressBook.getDetail();
        String userGeocoding = baiduMapsUtil.geocoding(address);
        if (userGeocoding == null) {
            throw new OrderBusinessException(MessageConstant.USER_ADDRESS_RESOLUTION_FAILED);
        }
        log.info("Get userGeocoding: {}", userGeocoding);

        // 判断距离是否在配送范围内
        Integer distance = baiduMapsUtil.direction(MERCHANT_GEOCODING, userGeocoding);
        if (distance == null) {
            log.info("Get distance: {}", distance);
            throw new OrderBusinessException(MessageConstant.NO_CYCLING_ROUTE);
        }
        if (distance < 0) {
            log.info("Get distance: {}", distance);
            throw new OrderBusinessException(MessageConstant.UNKNOWN_ERROR);
        }
        if (distance > baiduMapsProperties.getGeographicArea()) {
            throw new OrderBusinessException(MessageConstant.BEYOND_DELIVERY_SCOPE);
        }

        // 获取用户信息
        User user = userMapper.getUserByUserId(BaseContext.getCurrentId());
        if (user == null || user.getId() == null) {
            throw new OrderBusinessException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        // 获取用户购物车列表
        List<ShoppingCart> shoppingCarts = shoppingCartMapper.getShoppingCartByUserId(BaseContext.getCurrentId());
        if (shoppingCarts == null || shoppingCarts.size() == 0) {
            throw new OrderBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }

        // 封装order
        Orders order = Orders.builder()
                .number(UUID.randomUUID().toString().replace("-", ""))
                .status(Orders.PENDING_PAYMENT)
                .userId(BaseContext.getCurrentId())
                .addressBookId(ordersSubmitDTO.getAddressBookId())
                .orderTime(LocalDateTime.now())
                .payMethod(ordersSubmitDTO.getPayMethod())
                .payStatus(Orders.UN_PAID)
                .amount(ordersSubmitDTO.getAmount())
                .remark(ordersSubmitDTO.getRemark())
                .phone(addressBook.getPhone())
                .address(address)
                .userName(user.getName())
                .consignee(addressBook.getConsignee())
                .estimatedDeliveryTime(ordersSubmitDTO.getEstimatedDeliveryTime())
                .deliveryStatus(ordersSubmitDTO.getDeliveryStatus())
                .packAmount(ordersSubmitDTO.getPackAmount())
                .tablewareNumber(ordersSubmitDTO.getTablewareNumber())
                .tablewareStatus(ordersSubmitDTO.getTablewareStatus())
                .build();

        // 新增订单，需要返回主键id
        orderMapper.insertOrder(order);

        // 封装order_detail
        List<OrderDetail> orderDetails = shoppingCarts.stream().map((i) -> OrderDetail.builder()
                .name(i.getName())
                .image(i.getImage())
                .orderId(order.getId())
                .dishId(i.getDishId())
                .setmealId(i.getSetmealId())
                .dishFlavor(i.getDishFlavor())
                .number(i.getNumber())
                .amount(i.getAmount())
                .build()).collect(Collectors.toList());

        // 新增订单明细
        orderDetailMapper.insertOrderDetails(orderDetails);

        // 清空购物车
        shoppingCartMapper.deleteShoppingCartByUserId(BaseContext.getCurrentId());

        // 返回响应
        return OrderSubmitVO.builder()
                .id(order.getId())
                .orderAmount(order.getAmount())
                .orderNumber(order.getNumber())
                .orderTime(order.getOrderTime())
                .build();
    }

    /**
     * 微信支付
     *
     * @param orderNumber String
     * @return OrderPaymentVO
     */
    @Override
    public OrderPaymentVO wechatPayment(String orderNumber) {
        // 获取订单信息
        Orders order = orderMapper.getOrderByOrderNumber(orderNumber);
        if (order == null || order.getId() == null) {
            throw new BaseException(MessageConstant.FAILED_TO_OBTAIN_ORDER);
        }

        // 获取用户信息
        User user = userMapper.getUserByUserId(BaseContext.getCurrentId());
        if (user == null || user.getId() == null) {
            throw new BaseException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        // 调用微信下单接口
        JSONObject jsonObject = null;
        try {
            jsonObject = weChatPayUtil.pay(orderNumber, order.getAmount(), CommonConstant.SKY_TAKE_OUT, user.getOpenid());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (jsonObject == null) {
            throw new BaseException(MessageConstant.UNKNOWN_ERROR);
        }
        if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
            throw new OrderBusinessException("该订单已支付");
        }

        // 返回响应
        OrderPaymentVO orderPaymentVO = jsonObject.toJavaObject(OrderPaymentVO.class);
        orderPaymentVO.setPackageStr(jsonObject.getString("package"));
        return orderPaymentVO;
    }

    /**
     * 支付宝支付
     *
     * @param orderNumber String
     * @return OrderPaymentVO
     */
    @Override
    public OrderPaymentVO alipayPayment(String orderNumber) {
        // todo: 找时间写个支付宝支付
        return null;
    }

    /**
     * 处理微信支付完成的响应结果
     *
     * @param body String
     */
    @Override
    public void wechatPost(String body) throws GeneralSecurityException {
        // 密文解密
        JSONObject resultObject = JSON.parseObject(body);
        JSONObject resource = resultObject.getJSONObject("resource");
        String ciphertext = resource.getString("ciphertext");
        String nonce = resource.getString("nonce");
        String associatedData = resource.getString("associated_data");
        AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                ciphertext);

        // 获取订单号
        JSONObject jsonObject = JSON.parseObject(plainText);
        String outTradeNo = jsonObject.getString("out_trade_no");// 商户平台订单号
        String transactionId = jsonObject.getString("transaction_id");// 微信支付交易号
        log.info("商户平台订单号：{}", outTradeNo);
        log.info("微信支付交易号：{}", transactionId);

        // 获取订单信息
        Orders order = orderMapper.getOrderByOrderNumber(outTradeNo);

        // 修改订单信息
        orderMapper.updateByOrderId(Orders.builder()
                .id(order.getId())
                .status(Orders.TO_BE_CONFIRMED)
                .checkoutTime(LocalDateTime.now())
                .payStatus(Orders.PAID)
                .build());

        // 推送管理端来单提醒
        Map<String, Object> map = new HashMap<>();
        map.put("type", 1); // 消息类型，1表示来单提醒
        map.put("orderId", order.getId());
        map.put("content", "订单号：" + outTradeNo);
        webSocketService.sendToAllClient(JSON.toJSONString(map));
    }

    /**
     * 仅供apifox测试使用的支付接口
     *
     * @param orderNumber String
     */
    @Override
    public void apifoxPay(String orderNumber) {
        // 获取订单信息
        Orders order = orderMapper.getOrderByOrderNumber(orderNumber);
        if (order == null || order.getId() == null) {
            throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
        }
        if (Orders.PAID.equals(order.getPayStatus())) {
            throw new OrderBusinessException(MessageConstant.ORDER_PAYED);
        }

        // 修改订单信息
        orderMapper.updateByOrderId(Orders.builder()
                .id(order.getId())
                .status(Orders.TO_BE_CONFIRMED)
                .checkoutTime(LocalDateTime.now())
                .payStatus(Orders.PAID)
                .build());

        // 推送管理端来单提醒
        Map<String, Object> map = new HashMap<>();
        map.put("type", 1); // 消息类型，1表示来单提醒
        map.put("orderId", order.getId());
        map.put("content", "订单号：" + orderNumber);
        webSocketService.sendToAllClient(JSON.toJSONString(map));
    }

    /**
     * 获取历史订单
     *
     * @param ordersPageQueryDTO OrdersPageQueryDTO
     * @return PageResult
     */
    @Override
    public PageResult historyOrders(OrdersPageQueryDTO ordersPageQueryDTO) {
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        ordersPageQueryDTO.setUserId(BaseContext.getCurrentId());
        Page<Orders> page = (Page<Orders>) orderMapper.getOrderByOrdersPageQueryDTO(ordersPageQueryDTO);
        return new PageResult(page.getTotal(), page.getResult().stream().map((i) -> {
            OrderVO orderVO = new OrderVO();
            BeanUtils.copyProperties(i, orderVO);
            orderVO.setOrderDetailList(orderDetailMapper.getOrderDetailListByOrderId(orderVO.getId()));
            return orderVO;
        }).collect(Collectors.toList()));
    }

    /**
     * 查询订单详情
     *
     * @param orderId Long
     * @return OrderVO
     */
    @Override
    public OrderVO getOrderDetails(Long orderId) {
        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(orderMapper.getOrderByOrderId(orderId), orderVO);
        orderVO.setOrderDetailList(orderDetailMapper.getOrderDetailListByOrderId(orderId));
        return orderVO;
    }

    /**
     * 用户取消订单
     *
     * @param orderId Long
     */
    @Override
    public void userCancelOrder(Long orderId) {
        // 获取订单信息
        Orders order = orderMapper.getOrderByOrderId(orderId);
        if (order == null || order.getId() == null) {
            throw new OrderBusinessException(MessageConstant.FAILED_TO_OBTAIN_ORDER);
        }

        // 修改订单
        order.setStatus(Orders.CANCELLED);
        order.setCancelTime(LocalDateTime.now());
        orderMapper.updateByOrderId(order);
    }

    /**
     * 再来一单
     *
     * @param orderId Long
     */
    @Override
    public void repetitionOrder(Long orderId) {
        // 获取订单明细
        List<OrderDetail> orderDetails = orderDetailMapper.getOrderDetailListByOrderId(orderId);
        if (orderDetails == null || orderDetails.size() == 0) {
            throw new OrderBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }

        // 添加购物车
        shoppingCartMapper.insertShoppingCarts(orderDetails.stream().map(i -> ShoppingCart.builder()
                .name(i.getName())
                .image(i.getImage())
                .userId(BaseContext.getCurrentId())
                .dishId(i.getDishId())
                .setmealId(i.getSetmealId())
                .dishFlavor(i.getDishFlavor())
                .number(i.getNumber())
                .amount(i.getAmount())
                .createTime(LocalDateTime.now())
                .build()).collect(Collectors.toList()));
    }

    /**
     * 订单查询
     *
     * @param ordersPageQueryDTO OrdersPageQueryDTO
     * @return PageResult
     */
    @Override
    public PageResult searchOrders(OrdersPageQueryDTO ordersPageQueryDTO) {
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        Page<Orders> page = (Page<Orders>) orderMapper.getOrderByOrdersPageQueryDTO(ordersPageQueryDTO);
        return new PageResult(page.getTotal(), page.getResult().stream().map(i -> {
            OrderVO orderVO = new OrderVO();
            BeanUtils.copyProperties(i, orderVO);
            List<OrderDetail> orderDetails = orderDetailMapper.getOrderDetailListByOrderId(orderVO.getId());
            StringBuilder stringBuilder = new StringBuilder();
            orderDetails.forEach(j -> stringBuilder.append(j.getName()).append(", "));
            orderVO.setOrderDishes(stringBuilder.substring(0, stringBuilder.lastIndexOf(",")));
            return orderVO;
        }).collect(Collectors.toList()));
    }

    /**
     * 各个状态的订单数量统计
     *
     * @return OrderStatisticsVO
     */
    @Override
    public OrderStatisticsVO getStatistics() {
        return OrderStatisticsVO.builder()
                .toBeConfirmed(orderMapper.getOrderCountByStatus(Orders.TO_BE_CONFIRMED))
                .confirmed(orderMapper.getOrderCountByStatus(Orders.CONFIRMED))
                .deliveryInProgress(orderMapper.getOrderCountByStatus(Orders.DELIVERY_IN_PROGRESS))
                .build();
    }

    /**
     * 接单
     *
     * @param ordersConfirmDTO OrdersConfirmDTO
     */
    @Override
    public void confirmOrder(OrdersConfirmDTO ordersConfirmDTO) {
        orderMapper.updateByOrderId(Orders.builder()
                .id(ordersConfirmDTO.getId())
                .status(ordersConfirmDTO.getStatus())
                .build());
    }

    /**
     * 拒单
     *
     * @param ordersRejectionDTO OrdersRejectionDTO
     */
    @Override
    public void rejectionOrder(OrdersRejectionDTO ordersRejectionDTO) {
        orderMapper.updateByOrderId(Orders.builder()
                .id(ordersRejectionDTO.getId())
                .status(Orders.CANCELLED)
                .rejectionReason(ordersRejectionDTO.getRejectionReason())
                .cancelTime(LocalDateTime.now())
                .build());
    }

    /**
     * 商家取消订单
     *
     * @param ordersCancelDTO OrdersCancelDTO
     */
    @Override
    public void adminCancelOrder(OrdersCancelDTO ordersCancelDTO) {
        orderMapper.updateByOrderId(Orders.builder()
                .id(ordersCancelDTO.getId())
                .cancelReason(ordersCancelDTO.getCancelReason())
                .status(Orders.CANCELLED)
                .cancelTime(LocalDateTime.now())
                .build());
    }

    /**
     * 派送订单
     *
     * @param orderId Long
     */
    @Override
    public void deliveryOrder(Long orderId) {
        orderMapper.updateByOrderId(Orders.builder()
                .id(orderId)
                .status(Orders.DELIVERY_IN_PROGRESS)
                .build());
    }

    /**
     * 订单完成
     *
     * @param orderId Long
     */
    @Override
    public void completeOrder(Long orderId) {
        orderMapper.updateByOrderId(Orders.builder()
                .id(orderId)
                .status(Orders.COMPLETED)
                .deliveryTime(LocalDateTime.now())
                .build());
    }

    /**
     * 用户催单
     *
     * @param orderId Long
     */
    @Override
    public void reminderOrder(Long orderId) {
        // 获取订单信息
        Orders order = orderMapper.getOrderByOrderId(orderId);
        if (order == null) {
            throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
        }

        // 推送管理端用户催单
        Map<String, Object> map = new HashMap<>();
        map.put("type", 2);
        map.put("orderId", orderId);
        map.put("content", "订单号：" + order.getNumber());
        webSocketService.sendToAllClient(JSON.toJSONString(map));
    }

}
